package com.hb.repository.impl;

import com.hb.domain.Spitter;
import com.hb.repository.SpitterRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import java.util.List;

// 对于 @Repository 注解，它的作用与开发 Hibernate 上下文 Session 版本的 Repository 时是一致的。由于没有使用模板类来处理异常，所以我们需要为
// Repository 添加 @Repository 注解，这样 PersistenceExceptionTranslationPostProcessor 就会知道要将这个 bean 产生的异常转换成 Spring
// 的统一数据访问异常。
// 提醒一下，不管对于 JPA 还是 Hibernate，异常转换都不是强制要求的。如果你希望在 Repository 中抛出特定的 JPA 或 Hibernate 异常，只需将
// PersistenceExceptionTranslationPostProcessor 省略掉即可，这样原来的异常就会正常地处理。但是，如果使用了 Spring 的异常转换，你会将所有的数据
// 访问异常置于 Spring 的体系之下，这样以后切换持久化机制的话会更容易。
@Repository
// @Transactional 表明这个 Repository 中的持久化方法是在事务上下文中执行的。
@Transactional
public class JpaSpitterRepository implements SpitterRepository {
    /**
     * 注入 EntityManagerFactory
     * 因为 EntityManagerFactory 是 JPA 的东西，而 JPA 是 Java 标准持久化 API，也就是官方持久化 API，那么官方自然是 JavaEE。所以所属的包是 javax.XXX.XXX。
     * 还需要注意的是 EntityManagerFactory 属性，它使用了 @PersistenceUnit 注解，因此，Spring 会将 EntityManagerFactory 注入到 Repository 之中。有了
     * EntityManagerFactory 之后，JpaSpitterRepository 的方法就能使用它来创建 EntityManager 了，然后 EntityManager 可以针对数据库执行操作。
     */
    @PersistenceUnit
    private EntityManagerFactory emf;

    /**
     * 直接设置了 EntityManager 之后，在每个方法中就没有必要再通过 EntityManagerFactory 创建 EntityManager 了。尽管这种方式非常便利，但是你可能会担心注入
     * 的 EntityManager 会有线程安全性的问题。这里的真相是 @PersistenceContext 并不会真正注入 EntityManager ——至少，精确来讲不是这样的。它没有将真正的
     * EntityManager 设置给 Repository，而是给了它一个 EntityManager 的代理。真正的 EntityManager 是与当前事务相关联的那一个，如果不存在这样的 EntityManager
     * 的话，就会创建一个新的。这样的话，我们就能始终以线程安全的方式使用实体管理器。
     */
    @PersistenceContext
    private EntityManager em;

    @Override
    public long count() {
        return 0;
    }

    @Override
    public Spitter save(Spitter spitter) {
        return null;
    }

    @Override
    public Spitter findOne(long id) {
        return null;
    }

    @Override
    public Spitter findByUsername(String username) {
        return null;
    }

    @Override
    public List<Spitter> findAll() {
        return null;
    }

    /**
     * 这里有一个问题在于每个方法都会调用 createEntityManager()。除了引入易出错的重复代码以外，这还意味着每次调用 Repository 的方法时，都会创建一个新的
     * EntityManager。这种复杂性源于事务。如果我们能够预先准备好 EntityManager，那会不会更加方便呢？这里的问题在于 EntityManager 并不是线程安全的，一般来讲并不
     * 适合注入到像 Repository 这样共享的单例 bean 中。
     * @param spitter
     */
    public void addSpitter(Spitter spitter) {
        // 创建并使用 EntityManager
        emf.createEntityManager().persist(spitter);
    }

    public Spitter getSpitterById(long id) {
        return emf.createEntityManager().find(Spitter.class, id);
    }

    public void saveSpitter(Spitter spitter) {
        emf.createEntityManager().merge(spitter);
    }
}
